package com.hex.ds.hdtp.core.app.script.transfer.handler.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.hex.bigdata.hbda.dict.model.DictDetail;
import com.hex.bigdata.hbda.dict.service.DictDetailService;
import com.hex.ds.hdtp.core.app.common.constant.ScriptConvertContants;
import com.hex.ds.hdtp.core.app.common.enums.BasicListStatus;
import com.hex.ds.hdtp.core.app.common.enums.ScriptTableConsanguinityFrom;
import com.hex.ds.hdtp.core.app.data.table.dto.response.TableTransferFieldMapResponseDto;
import com.hex.ds.hdtp.core.app.data.table.dto.response.TableTransferListResponseDto;
import com.hex.ds.hdtp.core.app.data.table.service.ITableTransferFieldMapService;
import com.hex.ds.hdtp.core.app.data.table.service.ITableTransferListService;
import com.hex.ds.hdtp.core.app.license.activation.service.IFuncActivationService;
import com.hex.ds.hdtp.core.app.metadata.service.ISourceConfInfoService;
import com.hex.ds.hdtp.core.app.script.common.converter.IScriptTableConsanguinityConverter;
import com.hex.ds.hdtp.core.app.script.common.dto.response.ScriptTableConsanguinityResponseDto;
import com.hex.ds.hdtp.core.app.script.common.service.IScriptTableConsanguinityService;
import com.hex.ds.hdtp.core.app.script.transfer.converter.IScriptConvertConvert;
import com.hex.ds.hdtp.core.app.script.transfer.converter.IScriptConvertTargetListConverter;
import com.hex.ds.hdtp.core.app.script.transfer.dto.request.ScriptConvertSourceListRequestDto;
import com.hex.ds.hdtp.core.app.script.transfer.dto.request.ScriptConvertTargetListRequestDto;
import com.hex.ds.hdtp.core.app.script.transfer.dto.response.ScriptConvertSourceListResponseDto;
import com.hex.ds.hdtp.core.app.script.transfer.handler.IScriptConvertHandlerService;
import com.hex.ds.hdtp.core.app.script.transfer.service.IScriptConvertSourceListService;
import com.hex.ds.hdtp.core.app.script.transfer.service.IScriptConvertTargetListService;
import com.hex.ds.hdtp.core.app.script.transfer.service.IScriptConvertTaskService;
import com.hex.ds.hdtp.core.inf.common.utils.JdbcUtil;
import com.hex.ds.hdtp.core.inf.metadata.po.SourceConfInfoPo;
import com.hex.ds.hdtp.core.inf.script.transfer.po.ScriptConvertTaskPo;
import com.hex.ds.hdtp.core.inf.script.common.service.IScriptConvertService;
import com.hex.hdtp.scf.basic.convert.entity.ConvertConfig;
import com.hex.hdtp.scf.basic.convert.entity.ConvertResult;
import com.hex.hdtp.scf.basic.convert.entity.ScriptInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.File;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Package: com.hex.ds.hdtp.core.app.script.transfer.handler.impl
 * @ClassName ScriptConvertHandlerService
 * @Description: 脚本转换业务处理
 * @Author: wz.li
 * @Date 2023/8/14 17:05
 * @Version v2.0.2
 **/
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy, @Autowired})
public class ScriptConvertHandlerService implements IScriptConvertHandlerService {

    private final IScriptConvertTaskService scriptConvertTaskService;

    private final IScriptConvertSourceListService scriptConvertListService;

    private final DictDetailService dictDetailService;

    private static final String VARDATEDICTNAME = "script_convert_var_date";

    private static final String DBNAMESYSCODEDICTNAME = "script_convert_db_sys_code";

    private final ScriptConvertContants scriptConvertContants;

    private final IScriptConvertConvert scriptConvertConvert;

    private final IScriptConvertService scriptConvertService;

    private final ISourceConfInfoService sourceConfInfoService;

    private final IScriptTableConsanguinityService scriptTableConsanguinityService;

    private final IScriptTableConsanguinityConverter scriptTableConsanguinityConverter;

    private final IScriptConvertTargetListService scriptConvertTargetListService;

    private final IScriptConvertTargetListConverter scriptConvertTargetListConverter;

    private final ITableTransferFieldMapService tableTransferFieldMapService;

    private final ITableTransferListService tableTransferListService;

    private final IFuncActivationService funcActivationService;

    /*
     * @Method: scriptConvert <br>
     * @Param: [scriptConvertList] <br>
     * @Return: void <br>
     * @Description：脚本转换<br>
     * @Author： wz.li<br>
     * @Date： 2023/5/24 17:52 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    @Async("scriptConvertAsyncTaskExecutor")
    public void scriptConvertHandler(ScriptConvertSourceListResponseDto scriptConvertList, ScriptConvertTaskPo scriptConvertTask) {
        log.info("脚本【{}】开始转换，开始时间:{}", scriptConvertList.getSourceFileName(), DateUtil.now());
        modifyConvertListStatusById(scriptConvertList.getPkId(), BasicListStatus.PROCESSING.name(), null);
        try {
            ConvertConfig convertConfig = buildConvertConfig(scriptConvertList, scriptConvertTask);
            ConvertResult convertResult = scriptConvertService.scriptConvert(scriptConvertTask.getConvertType(), convertConfig);
            if (isConvertSuccess(convertResult)) {
                convertSuccessHandler(scriptConvertTask, scriptConvertList, convertResult);
            }
        } catch (Exception e) {
            log.error("脚本【{}】转换失败，失败原因:{}", scriptConvertList.getSourceFileName(), e.getMessage(), e);
            convertFailHandler(scriptConvertTask.getPkId(), scriptConvertList.getPkId(), e.getMessage());
        } finally {
            log.info("脚本【{}】结束转换，结束时间:{}", scriptConvertList.getSourceFileName(), DateUtil.now());
        }
    }

    /**
     * @Method scriptConvertType
     * @Param
     * @Return java.util.List<java.lang.String>
     * @Description 获取脚本转换插件类型
     * @Author gj.xu
     * @Date 2023/11/29 9:21
     * @Version V2.0
     */
    @Override
    public List<String> scriptConvertType() {
        return scriptConvertService.scriptConvertType();
    }

    private ConvertConfig buildConvertConfig(ScriptConvertSourceListResponseDto scriptConvertList, ScriptConvertTaskPo scriptConvertTask) {
        List<DictDetail> varDateList = dictDetailService.findDictDetailByDictName(VARDATEDICTNAME);
        List<DictDetail> dbNameSysCodeList = dictDetailService.findDictDetailByDictName(DBNAMESYSCODEDICTNAME);
        Map<String, String> varDateMap = varDateList.stream().collect(Collectors.toMap(DictDetail::getLabel, DictDetail::getValue));
        Map<String, String> dbNameSysCodeMap = dbNameSysCodeList.stream().collect(Collectors.toMap(DictDetail::getLabel, DictDetail::getValue));
        ConvertConfig convertConfig = scriptConvertConvert.oracleScriptConvert(scriptConvertList, scriptConvertContants);
        convertConfig.setTargetFilePath(scriptConvertTask.getConvertFilePath());
        convertConfig.setVarDate(varDateMap);
        convertConfig.setDbNameSysCodeMap(dbNameSysCodeMap);
        // TODO 目前脚本转换 ORACLE-IMPALA、ORACLE-MYSQL 已支持 connection
        if (scriptConvertTask.getConvertType().startsWith("ORACLE-")) {
            convertConfig.setSourceConnection(getJdbcConnection(scriptConvertTask.getSourceConnCode()));
            convertConfig.setTargetConnection(getJdbcConnection(scriptConvertTask.getTargetConnCode()));
        }

        // 填充该脚本涉及的表映射信息
        List<ScriptTableConsanguinityResponseDto> scriptTableConsanguinity = scriptTableConsanguinityService.queryList(scriptConvertList.getPkId(), ScriptTableConsanguinityFrom.SCRIPT_CONVERT_SOURCE.getName());
        scriptTableConsanguinity.forEach(t -> {
            String[] parts = t.getTableName().split("\\.", 2);
            TableTransferListResponseDto tableTransferListResponseDto = tableTransferListService.queryLatestTransferTable(parts[0], parts[1]);
            List<TableTransferFieldMapResponseDto> tableTransferFieldMap = tableTransferFieldMapService.queryListByListPkId(tableTransferListResponseDto.getPkId());
            String origTableName = tableTransferListResponseDto.getSourceDbName() + "." + tableTransferListResponseDto.getSourceTableName();
            String tableName = tableTransferListResponseDto.getTargetDbName() + "." + tableTransferListResponseDto.getTargetTableName();
            tableTransferFieldMap.forEach(f -> {
                String origFieldName = f.getSourceFieldName();
                String fieldName = f.getTargetFieldName();
                String origFieldType = f.getSourceFieldLength() == null ? f.getSourceFieldType() : (f.getSourceFieldType() + "(" + f.getSourceFieldLength() + ")");
                String fieldType = f.getTargetFieldLength() == null ? f.getTargetFieldType() : (f.getTargetFieldType() + "(" + f.getTargetFieldLength() + ")");
                convertConfig.addTableInfoDetail(origTableName, tableName, origFieldName, fieldName, fieldType, origFieldType);
            });
        });

        return convertConfig;
    }

    private Connection getJdbcConnection(String sourceCode) {
        List<SourceConfInfoPo> sourceConfInfoPoList = sourceConfInfoService.queryListBySourceCode(sourceCode);
        return JdbcUtil.getJdbcConnection(sourceConfInfoPoList);
    }

    private boolean isConvertSuccess(ConvertResult convertResult) {
        if (ConvertResult.RESPONSE_CODE.CENTER_500.getCode() == convertResult.getCode()) {
            throw new RuntimeException("转换失败," + convertResult.getMsg());
        }
        return true;
    }

    private void convertSuccessHandler(ScriptConvertTaskPo scriptConvertTask, ScriptConvertSourceListResponseDto scriptConvertList, ConvertResult convertResult) {
        modifyConvertListStatusById(scriptConvertList.getPkId(), BasicListStatus.SUCCESS.name(), null);
        createConvertTargetFileList(scriptConvertTask, scriptConvertList, convertResult);
        scriptConvertTaskService.modifySuccessNum(scriptConvertList.getConvertTaskPkId());
        funcActivationService.deductScriptConvertSurplus(scriptConvertList);
    }

    private void createConvertTargetFileList(ScriptConvertTaskPo scriptConvertTask, ScriptConvertSourceListResponseDto scriptConvertList, ConvertResult convertResult) {
        String convertFilePath = scriptConvertTask.getConvertFilePath() + File.separator;
        // pkb文件转换后，转换后文件放在转换目录下的pkb文件名目录下
        if (convertResult.getScriptInfos().size() > 1) {
            convertFilePath = convertFilePath + scriptConvertList.getSourceFileName().split("\\.")[0] + File.separator;
        }
        for (ScriptInfo scriptInfo : convertResult.getScriptInfos()) {
            // 转换后文件名，当未返回文件类型，则赋予py
            String convertFileName = scriptInfo.getScriptName().contains(".") ? scriptInfo.getScriptName() : (scriptInfo.getScriptName() + ".py");
            File convertFile = new File(convertFilePath + convertFileName);
            String convertTargetListId = addConvertFileList(scriptConvertList, convertFile);
            addSourceTableConsanguinity(scriptInfo, scriptConvertList);
            addTargetTableConsanguinity(convertTargetListId, scriptInfo, scriptConvertList, convertFile);
        }
    }

    private String addConvertFileList(ScriptConvertSourceListResponseDto scriptConvertList, File convertFile) {
        ScriptConvertTargetListRequestDto requestDto = scriptConvertTargetListConverter.toDto(scriptConvertList, convertFile);
        requestDto.setConvertFileSize(convertFile.length() + "");
        return scriptConvertTargetListService.add(requestDto);
    }

    /**
     * @Method addTargetTableConsanguinity
     * @Param
     * @Param convertTargetListId
     * @Param scriptInfo
     * @Param scriptConvertList
     * @Param convertFile
     * @Return void
     * @Description 添加脚本转换前血缘
     * @Author wz.li
     * @Date 2023/10/13 15:33
     * @Version V2.0
     */
    private void addSourceTableConsanguinity(ScriptInfo scriptInfo, ScriptConvertSourceListResponseDto scriptConvertList) {
        if (CollectionUtil.isNotEmpty(scriptInfo.getOrigSrcTableContent())) {
            for (String tableName : scriptInfo.getOrigSrcTableContent()) {
                scriptTableConsanguinityService.add(scriptTableConsanguinityConverter.toScriptOrigTableConsanguinityForSource(scriptConvertList, tableName));
            }
        }
        if (CollectionUtil.isNotEmpty(scriptInfo.getOrigTargetTableContent())) {
            for (String tableName : scriptInfo.getOrigTargetTableContent()) {
                scriptTableConsanguinityService.add(scriptTableConsanguinityConverter.toScriptOrigTableConsanguinityForTarget(scriptConvertList, tableName));
            }
        }
    }

    /**
     * @Method addTargetTableConsanguinity
     * @Param
     * @Param convertTargetListId
     * @Param scriptInfo
     * @Param scriptConvertList
     * @Param convertFile
     * @Return void
     * @Description 添加脚本转换后血缘
     * @Author wz.li
     * @Date 2023/10/13 15:33
     * @Version V2.0
     */
    private void addTargetTableConsanguinity(String convertTargetListId, ScriptInfo scriptInfo,
                                             ScriptConvertSourceListResponseDto scriptConvertList, File convertFile) {
        if (CollectionUtil.isNotEmpty(scriptInfo.getSrcTableContent())) {
            for (String tableName : scriptInfo.getSrcTableContent()) {
                scriptTableConsanguinityService.add(scriptTableConsanguinityConverter
                        .toScriptTableConsanguinityForSource(
                                scriptConvertList.getConvertTaskPkId(),
                                convertTargetListId,
                                convertFile.getName(),
                                tableName));
            }
        }
        if (CollectionUtil.isNotEmpty(scriptInfo.getTargetTableContent())) {
            for (String tableName : scriptInfo.getTargetTableContent()) {
                scriptTableConsanguinityService.add(scriptTableConsanguinityConverter
                        .toScriptTableConsanguinityForTarget(
                                scriptConvertList.getConvertTaskPkId(),
                                convertTargetListId,
                                convertFile.getName(),
                                tableName));
            }
        }
    }

    private void convertFailHandler(String convertTaskPkId, String convertSourceListPkId, String message) {
        modifyConvertListStatusById(convertSourceListPkId, BasicListStatus.FAIL.name(), message);
        scriptConvertTaskService.modifyFailNum(convertTaskPkId);
    }

    /*
     * @Method: modifyConvertListStatusById <br>
     * @Param: [pkId, status, convertLog] <br>
     * @Return: void <br>
     * @Description：根据转换清单转换状态信息<br>
     * @Author： wz.li<br>
     * @Date： 2023/8/14 16:01 <br>
     * @Version： V2.0.2<br>
     */
    private void modifyConvertListStatusById(String pkId, String status, String convertLog) {
        ScriptConvertSourceListRequestDto scriptConvertList = new ScriptConvertSourceListRequestDto();
        scriptConvertList.setPkId(pkId).setStatus(status).setConvertLog(convertLog);
        if (BasicListStatus.PROCESSING.name().equals(status)) {
            scriptConvertList.setConvertStartTs(DateUtil.now());
        } else {
            scriptConvertList.setConvertEndTs(DateUtil.now());
        }
        scriptConvertListService.modifyById(scriptConvertList);
    }

}
